package com.taokatao.cardmee.common.widget;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.taokatao.cardmee.C;
import com.taokatao.cardmee.R;
import com.taokatao.cardmee.Utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class PagerLayout extends FrameLayout {
    public interface OnStateChangedListener {
        void upToCurrent();

        void backToLast(boolean anim);

        void touchMove(float progress);
    }

    private List<OnStateChangedListener> callbacks = new ArrayList<>();

    public void addOnStateChangedListener(OnStateChangedListener callback) {
        if (!callbacks.contains(callback)) {
            callbacks.add(callback);
        }
    }

    private int mWidth;

    private final Stack<FrameLayout> pageContainers = new Stack<>();
    private FrameLayout layCurrPageWrap;
    private FrameLayout layLastPageWrap;

    private float downX;
    private float downCurrPageTransX, downLastPageTransX;

    private float interceptDownX;
    private float touchSclop;
    private float backTouchWidth;

    private boolean isAnim;
    private boolean touchMove;
    private boolean slidingPage;
    private float tmpMoveProgress; // 0 ~ 1
    private boolean canTouchable = true;

    private FrameLayout layContainerWrap;
    private FrameLayout layShadow;
    private View viewShadow;
    private boolean shadow = true;

    private static final int ANIMATION_TIME = C.settings.ANIMATION_TIME;

    private final TypeEvaluator<Integer> ce = new TypeEvaluator<Integer>() {
        @Override
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            int startColor = startValue;
            int endColor = endValue;

            int alpha = (int) (Color.alpha(startColor) + fraction * (Color.alpha(endColor) - Color.alpha(startColor)));
            int red = (int) (Color.red(startColor) + fraction * (Color.red(endColor) - Color.red(startColor)));
            int green = (int) (Color.green(startColor) + fraction * (Color.green(endColor) - Color.green(startColor)));
            int blue = (int) (Color.blue(startColor) + fraction * (Color.blue(endColor) - Color.blue(startColor)));

            return Color.argb(alpha, red, green, blue);
        }
    };

    public PagerLayout(Context context) {
        this(context, null);
    }

    public PagerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        touchSclop = ViewConfiguration.get(context).getScaledTouchSlop();
        layContainerWrap = new FrameLayout(context);
        addView(layContainerWrap);
        layShadow = new FrameLayout(context);
        addView(layShadow);

        viewShadow = new View(context);
        viewShadow.setBackgroundResource(R.drawable.frame_shpe_pager_shadow);
        final float scale = context.getResources().getDisplayMetrics().density;
        int shadowWidth = (int) (16 * scale + 0.5f);
        LayoutParams lpShadow = new LayoutParams(shadowWidth, ViewGroup.LayoutParams.MATCH_PARENT);
        lpShadow.gravity = Gravity.RIGHT;
        viewShadow.setLayoutParams(lpShadow);
        layShadow.addView(viewShadow);
        viewShadow.setAlpha(0f);
    }

    public void pushNewContent(View view) {
        pushNewContent(view, true);
    }

    public void pushNewContent(View view, boolean anim) {
        final FrameLayout layContentWrap = new FrameLayout(getContext());
        layContentWrap.addView(view);

        View lastWrap = null;
        if (!pageContainers.isEmpty()) {
            lastWrap = pageContainers.peek();
        }

        final View mLastContentWrap = lastWrap;
        if (anim) {
            ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(ANIMATION_TIME);
            animator.addUpdateListener(new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (Float) animation.getAnimatedValue();
                    if (mLastContentWrap != null) {
                        mLastContentWrap.setTranslationX(-(mWidth / 2) * value);
                    }
                    layContentWrap.setTranslationX(getWidth() * (1 - value));
                }
            });
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    isAnim = false;
                    if (mLastContentWrap != null) {
                        mLastContentWrap.setVisibility(View.GONE);
                    }
                }
            });
            animator.start();
            isAnim = true;
        } else {
            if (mLastContentWrap != null) {
                mLastContentWrap.setVisibility(View.GONE);
                final float value = 1;
                mLastContentWrap.setTranslationX(-(mWidth / 2) * value);
            }
        }

        pageContainers.add(layContentWrap);
        layContainerWrap.addView(layContentWrap);
    }

    public View popToLastContent() {
        return popToLastContent(0, true);
    }

    private View popToLastContent(float progress) {
        return popToLastContent(progress, true);
    }

    public View popToLastContent(float progress, final boolean anim) {
        if (pageContainers.size() < 2) {
            // Not enough item
            return null;
        }

        Utils.hideSoftInputFromWindowAlways((Activity) getContext());

        final FrameLayout currContentWrap = pageContainers.pop();
        final FrameLayout lastContentWrap = pageContainers.peek();

        if (anim) {
            ValueAnimator animator = ValueAnimator.ofFloat(progress, 1).setDuration(ANIMATION_TIME);
            animator.addUpdateListener(new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (Float) animation.getAnimatedValue();

                    layShadow.setTranslationX(-mWidth + mWidth * value);
                    layShadow.setBackgroundColor(shadow ? ce.evaluate(value, 0x16000000, 0x00000000) : 0x00000000);
                    viewShadow.setAlpha(shadow ? (1 - value) : 0);

                    currContentWrap.setTranslationX(mWidth * value);
                    lastContentWrap.setTranslationX(-mWidth / 2 * (1 - value));
                }
            });
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    super.onAnimationStart(animation);
                    isAnim = true;
                    lastContentWrap.setVisibility(View.VISIBLE);
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    isAnim = false;

                    layShadow.setTranslationX(0);
                    layShadow.setBackgroundColor(0x00000000);
                    viewShadow.setAlpha(0f);

                    View real = currContentWrap.getChildAt(0);
                    if (real != null && real instanceof OnStateChangedListener) {
                        ((OnStateChangedListener) real).backToLast(anim);
                    }

                    layContainerWrap.removeView(currContentWrap);


                }
            });
            animator.start();
        } else {
            View real = currContentWrap.getChildAt(0);
            if (real != null && real instanceof OnStateChangedListener) {
                ((OnStateChangedListener) real).backToLast(anim);
            }
            layContainerWrap.removeView(currContentWrap);
            lastContentWrap.setTranslationX(0);
            lastContentWrap.setVisibility(View.VISIBLE);
        }

        for (OnStateChangedListener c : callbacks) {
            c.backToLast(anim);
        }
        return lastContentWrap;
    }

    private void returnToCurrentContent(float progress) {
        if (pageContainers.size() < 2) {
            // Not enough item
            return;
        }

        final FrameLayout curr = pageContainers.get(pageContainers.size() - 1);
        final FrameLayout last = pageContainers.get(pageContainers.size() - 2);
        last.setVisibility(View.VISIBLE);

        ValueAnimator animator = ValueAnimator.ofFloat(progress, 0).setDuration(ANIMATION_TIME);
        animator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                final float progress = (Float) animation.getAnimatedValue();

                layShadow.setTranslationX(-mWidth + progress * mWidth);
                layShadow.setBackgroundColor(shadow ? ce.evaluate(progress, 0x16000000, 0x00000000) : 0x00000000);
                viewShadow.setAlpha(shadow ? progress : 0);

                curr.setTranslationX(mWidth * progress);
                last.setTranslationX(-mWidth / 2 * (1 - progress));
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                last.setVisibility(View.GONE);

                layShadow.setTranslationX(-mWidth);
                layShadow.setBackgroundColor(shadow ? 0x16000000 : 0x00000000);
                viewShadow.setAlpha(0);

                View real = curr.getChildAt(0);
                if (real != null && real instanceof OnStateChangedListener) {
                    ((OnStateChangedListener) real).upToCurrent();
                }

                isAnim = false;
            }
        });
        animator.start();
        isAnim = true;

        for (OnStateChangedListener c : callbacks) {
            c.upToCurrent();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        backTouchWidth = mWidth / 4;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                interceptDownX = ev.getRawX();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!isOnlyOnePageOrEmpty() && interceptDownX < backTouchWidth && Math.abs(ev.getRawX() - interceptDownX) > touchSclop) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        if (!isAnim && canTouchable) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    return true;
                case MotionEvent.ACTION_MOVE:
                    if (!touchMove && !isOnlyOnePageOrEmpty()) {
                        touchMove = true;
                        downX = event.getRawX();
                        layCurrPageWrap = pageContainers.get(pageContainers.size() - 1);
                        layLastPageWrap = pageContainers.get(pageContainers.size() - 2);
                        downCurrPageTransX = layCurrPageWrap.getTranslationX();
                        downLastPageTransX = layLastPageWrap.getTranslationX();
                    }

                    if (layLastPageWrap != null && layLastPageWrap.getVisibility() != View.VISIBLE) {
                        layLastPageWrap.setVisibility(View.VISIBLE);
                    }

                    if (!slidingPage) {
                        if (!isOnlyOnePageOrEmpty() && downX < backTouchWidth && Math.abs(event.getRawX() - downX) > touchSclop) {
                            slidingPage = true;
                        }
                    } else {
                        float currPageResult = downCurrPageTransX + event.getRawX() - downX;
                        if (currPageResult < 0) {
                            currPageResult = 0;
                        }
                        layCurrPageWrap.setTranslationX(currPageResult);

                        layShadow.setTranslationX(-mWidth + currPageResult);
                        layShadow.setBackgroundColor(shadow ? ce.evaluate(currPageResult / mWidth, 0x16000000, 0x00000000) : 0x00000000);
                        viewShadow.setAlpha(shadow ? (1 - currPageResult / mWidth) : 0);

                        float lastResult = downLastPageTransX + (event.getRawX() - downX) / 2;
                        if (lastResult > 0) {
                            lastResult = 0;
                        }
                        layLastPageWrap.setTranslationX(lastResult);
                        tmpMoveProgress = currPageResult / mWidth;

                        for (OnStateChangedListener c : callbacks) {
                            c.touchMove(tmpMoveProgress);
                        }

                        View real = layCurrPageWrap.getChildAt(0);
                        if (real != null && real instanceof OnStateChangedListener) {
                            ((OnStateChangedListener) real).touchMove(tmpMoveProgress);
                        }
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    if (!isOnlyOnePageOrEmpty()) {
                        if (slidingPage) {
                            if (event.getRawX() - downX > backTouchWidth) {
                                popToLastContent(tmpMoveProgress);
                                tmpMoveProgress = 1;
                            } else {
                                returnToCurrentContent(tmpMoveProgress);
                                tmpMoveProgress = 0;
                            }
                        } else {
                            if (layLastPageWrap != null && layLastPageWrap.getVisibility() != View.GONE) {
                                layLastPageWrap.setVisibility(View.GONE);
                            }
                        }
                    }
                    touchMove = false;
                    slidingPage = false;
                    break;
            }
        }
        return super.onTouchEvent(event);
    }

    public boolean isOnlyOnePageOrEmpty() {
        return pageContainers.size() <= 1;
    }

    public int currLevel() {
        return pageContainers.size();
    }

    public boolean isDoingAnim() {
        return isAnim;
    }

    public void setCanTouchable(boolean isCanTouchable) {
        canTouchable = isCanTouchable;
    }

    public void setShowShadow(boolean showShadow) {
        shadow = showShadow;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        callbacks.clear();
    }
}
